Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a derive macro for setting fields #105

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

LukaOber
Copy link
Collaborator

I documented the macro a bit in the charming-derive/src/lib.rs file. A well formatted version can be seen when using cargo doc. You can check what the macro expands to when using cargo expand --test derive_test in the charming-derive folder.

Current implementation

#[derive(Serialize, Debug, Clone)]
struct Chart {
    #[serde(skip_serializing_if = "Vec::is_empty")]
    title: Vec<Title>,
    #[serde(skip_serializing_if = "Option::is_none")]
    tooltip: Option<Tooltip>,

    // many fields cut for brevity
}

// For setting these fields with the setter pattern we would need to implement the following
// methods manually when we are not using the derive macro
impl Chart {
    pub fn new() -> Self {
        Self {
            title: Vec::new(),
            tooltip: None,
        }
    }
    pub fn title<T: Into<Title>>(mut self, title: T) -> Self {
        self.title.push(title.into());
        self
    }
    pub fn tooltip<T: Into<Tooltip>>(mut self, tooltip: T) -> Self {
        self.tooltip = Some(tooltip.into());
        self
    }

    // many methods cut for brevity
}

// Example chart creation
let chart = Chart::new().title(Title::new().text("Title")).tooltip(Tooltip::new());

Implementation with the macro

#[derive(Serialize, Debug, Clone, CharmingSetter)]
struct Chart {
    #[serde(skip_serializing_if = "Vec::is_empty")]
    title: Vec<Title>,
    #[serde(skip_serializing_if = "Option::is_none")]
    tooltip: Option<Tooltip>,

    // many fields cut for brevity
}
// The setter methods from the example above now get implemented automatically by CharmingSetter

// Example chart creation
let chart = Chart::new().title(Title::new().text("Title")).tooltip(Tooltip::new());

I also added two field attributes charming_type and charming_skip_setter, their uses can be seen below

#[derive(Serialize, Debug, Clone, CharmingSetter)]
#[serde(rename_all = "camelCase")]
pub struct Line {
    // charming_type gets used here to provide the value "line" as the default value for
    // the field 'type_' when calling Line::new() and also removes the method to set the field
    #[serde(rename = "type")]
    #[charming_type = "line"]
    type_: String,

    // cut for brevity

    // charming_skip_setter gets used here to remove the default implementation of the setter
    // method for this field, a manual method to set this field needs to be provided instead
    #[serde(skip_serializing_if = "Vec::is_empty")]
    #[charming_skip_setter]
    data: Vec<DataPoint>,
}

impl Line {
    pub fn data<D: Into<DataPoint>>(mut self, data: Vec<D>) -> Self {
        self.data = data.into_iter().map(|d| d.into()).collect();
        self
    }
}

This is just a draft. I will implement more tests first and then open another PR where we derive the new macro on all applicable structs. Just want to get some feedback on potential issues and thoughts first.

Furthermore, I expect little to no breaking changes for the user when switching to this implementation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant